package goinput

// #include <sys/ioctl.h>
import "C"

import (
	"errors"
	"syscall"
	"unsafe"

	"notabug.org/_percival/gostd"
)

// Description of data input.
type Input struct {
	// Input descriptor.
	Fd int
	// Attribute state.
	isRaw bool
	// Attribute backup.
	iosBackup syscall.Termios
	// Manual input protection.
	Mutex gostd.Toggle
}

// Return input mode.
func (input *Input) IsRaw() bool {
	return input.isRaw
}

// Set input to raw mode.
func (input *Input) SetRaw() (err error) {
	var iosRaw syscall.Termios

	if input.isRaw {
		return errors.New("already in raw mode")
	}

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(input.Fd), uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&input.iosBackup))); errno != 0 {
		return errors.New("failed to get attributes: " + syscall.Errno(errno).Error())
	}

	iosRaw = input.iosBackup

	iosRaw.Lflag &^= syscall.ICANON | syscall.IEXTEN | syscall.ISIG | syscall.ECHO
	iosRaw.Iflag &^= syscall.ICRNL | syscall.IXON
	iosRaw.Oflag &^= syscall.OPOST

	iosRaw.Cc[syscall.VTIME] = 0
	iosRaw.Cc[syscall.VMIN] = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(input.Fd), uintptr(C.TCSETSF), uintptr(unsafe.Pointer(&iosRaw))); errno != 0 {
		return errors.New("failed to set attributes: " + syscall.Errno(errno).Error())
	}

	input.isRaw = true

	return
}

// Read input and write the result to data.
func (input *Input) Read(data *Data) (err error) {
	data.len, err = syscall.Read(input.Fd, data.Slice)

	return
}

// Set input to default mode.
func (input *Input) ResetRaw() (err error) {
	if !input.isRaw {
		return errors.New("already in default mode")
	}

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(input.Fd), uintptr(C.TCSETSF), uintptr(unsafe.Pointer(&input.iosBackup))); errno != 0 {
		return errors.New("failed to set attributes: " + syscall.Errno(errno).Error())
	}

	input.isRaw = false

	return
}
